package com.wans.guli.cart.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wans.guli.cart.feign.ProductFeignClient;
import com.wans.guli.cart.interceptor.CartUserKeyCheckInterceptor;
import com.wans.guli.cart.service.CartService;
import com.wans.guli.cart.vo.*;
import com.wans.guli.common.utils.R;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    @Qualifier("cartExecutor")
    private Executor executor;

    /**
     * 添加商品到购物车
     *
     * @param skuId 商品id
     * @param num   数量
     * @return 添加成功后的购物项
     */
    @Override
    @SneakyThrows
    public CartItemVo addCartItem(Long skuId, Integer num) {
        // 获取用户会话在redis中对应的key
        String redisCartKey = getCartKey();
        if (redisCartKey == null) return null; // 没有会话

        // 获取一个操作
        BoundHashOperations<String, String, String> boundHashOps = stringRedisTemplate.boundHashOps(redisCartKey);

        // 获取购物项
        String cartItemById = boundHashOps.get(skuId.toString());
        // 查询购物车中该商品
        if (StrUtil.isNotBlank(cartItemById)) {
            // redis中存在该商品
            CartItemVo cartItem = objectMapper.readValue(cartItemById, CartItemVo.class);
            // 修改数量
            cartItem.setCount(cartItem.getCount() + num);
            // 重新写入redis
            boundHashOps.put(skuId.toString(), objectMapper.writeValueAsString(cartItem));

            return cartItem;
        }

        // redis中不存在该商品
        if (StrUtil.isBlank(cartItemById)) {
            // redis中不存在该商品
            CartItemVo cartItem = new CartItemVo();

            CompletableFuture<Void> skuInfoSetFutureTask = CompletableFuture.runAsync(() -> {
                // 远程调用商品服务查询商品信息
                R skuInfoToR = productFeignClient.infoById(skuId);
                SkuInfoVo skuInfo = skuInfoToR.getData(SkuInfoVo.class);
                cartItem.setSkuId(skuId);
                cartItem.setTitle(skuInfo.getSkuTitle());
                cartItem.setImage(skuInfo.getSkuDefaultImg());
                cartItem.setPrice(skuInfo.getPrice());
                cartItem.setCount(num);
                cartItem.setCheck(true);
            }, executor);


            CompletableFuture<Void> skuSaleAttrsSetFutureTask = CompletableFuture.runAsync(() -> {
                // 远程调用商品服务查询商品销售属性信息
                R skuSaleAttrValuesToR = productFeignClient.infoListBySkuId(skuId);
                List<SkuSaleAttrValueVO> skuSaleAttrValueVOs = skuSaleAttrValuesToR.getListData(SkuSaleAttrValueVO.class);
                // 清晰数据 封装为 属性名+值 字符
                List<String> skuSaleAttrs = skuSaleAttrValueVOs.stream()
                        .map(sale -> sale.getAttrName() + ":  " + sale.getAttrValue())
                        .collect(Collectors.toList());
                cartItem.setSkuAttrValues(skuSaleAttrs);
            }, executor);

            // 等待所有任务完成
            CompletableFuture.allOf(skuInfoSetFutureTask, skuSaleAttrsSetFutureTask).get();

            boundHashOps.put(skuId.toString(), objectMapper.writeValueAsString(cartItem));

            return cartItem;
        }

        // 获取购物项失败 - 未知错误 - 想不到怎么运行才会走到这一步
        return null;
    }

    /**
     * 根据skuId获取购物项
     *
     * @param skuId 商品id
     * @return 购物项
     */
    @Override
    @SneakyThrows
    public CartItemVo getCartItemBySkuId(Long skuId) {
        // 获取用户会话在redis中对应的key
        String redisCartKey = getCartKey();
        if (redisCartKey == null) return null; // 没有会话
        // 获取一个操作
        BoundHashOperations<String, String, String> boundHashOps = stringRedisTemplate.boundHashOps(redisCartKey);
        // 获取购物项
        String cartItemByIdToStr = boundHashOps.get(skuId.toString());
        if (StrUtil.isNotBlank(cartItemByIdToStr)) {
            // 返回购物项
            return objectMapper.readValue(cartItemByIdToStr, CartItemVo.class);
        }

        // 获取购物项失败 - 未知错误 - 没有该skuId的购物项
        return null;
    }

    /**
     * 获取购物车 - 列表
     *
     * @return 购物车
     */
    @Override
    @SneakyThrows
    public CartVo getCart() {
        // 合并购物项
        checkUserCookieCart();

        // 获取用户会话在redis中对应的key
        String redisCartKey = getCartKey();
        if (redisCartKey == null) return null; // 没有会话

        // 获取一个操作hash
        BoundHashOperations<String, String, String> boundHashOps = stringRedisTemplate.boundHashOps(redisCartKey);
        // 获取所有购物项
        List<String> cartItems = boundHashOps.values();
        if (CollUtil.isEmpty(cartItems)) return null; // 购物项为空
        // 购物车
        CartVo cart = new CartVo();
        // 初始化
        cart.setItems(new ArrayList<>());
        // 获取购物项
        for (String cartItemStr : cartItems) {
            // 添加购物项
            cart.getItems().add(objectMapper.readValue(cartItemStr, CartItemVo.class));
        }
        //TODO 远程调用 - 获取优惠 - 暂时设置为0
        cart.setReduce(BigDecimal.ZERO);


        return cart;
    }

    /**
     * 修改购物项选中状态
     *
     * @param skuId   商品id
     * @param checked 选中状态
     */
    @Override
    @SneakyThrows
    public void updateCheckStatus(Long skuId, Integer checked) {
        // 获取用户会话在redis中对应的key
        String redisCartKey = getCartKey();
        if (redisCartKey == null) return; // 没有会话

        // 获取一个操作hash
        BoundHashOperations<String, String, String> boundHashOps = stringRedisTemplate.boundHashOps(redisCartKey);
        String cartItemByIdToStr = boundHashOps.get(skuId.toString());
        if (StrUtil.isNotBlank(cartItemByIdToStr)) {
            // 获取购物项
            CartItemVo cartItem = objectMapper.readValue(cartItemByIdToStr, CartItemVo.class);
            // 修改选中状态 - 逻辑取反
            cartItem.setCheck(!cartItem.getCheck());
            // 重新写入redis
            boundHashOps.put(skuId.toString(), objectMapper.writeValueAsString(cartItem));
        }
    }

    /**
     * 修改购物项数量
     *
     * @param skuId 商品id
     * @param num   数量
     */
    @Override
    @SneakyThrows
    public void updateCartSkuNum(Long skuId, Integer num) {
        // 获取用户会话在redis中对应的key
        String redisCartKey = getCartKey();
        if (redisCartKey == null) return; // 没有会话

        // 获取一个操作hash
        BoundHashOperations<String, String, String> boundHashOps = stringRedisTemplate.boundHashOps(redisCartKey);
        String cartItemByIdToStr = boundHashOps.get(skuId.toString());
        if (StrUtil.isNotBlank(cartItemByIdToStr)) {
            // 获取购物项
            CartItemVo cartItem = objectMapper.readValue(cartItemByIdToStr, CartItemVo.class);
            // 修改数量
            cartItem.setCount(num);
            // 重新写入redis
            boundHashOps.put(skuId.toString(), objectMapper.writeValueAsString(cartItem));
        }
    }

    /**
     * 删除购物项
     *
     * @param skuId 商品id
     */
    @Override
    public void removeById(Long skuId) {
        // 获取用户会话在redis中对应的key
        String redisCartKey = getCartKey();
        if (redisCartKey == null) return; // 没有会话

        // 删除购物项 根据skuId
        stringRedisTemplate.opsForHash().delete(redisCartKey, skuId.toString());
    }

    /**
     * 获取所有选中的购物项skuId
     *
     * @return 选中的购物项skuId
     */
    @Override
    public List<CartItemVo> getCartSkuIds() {
        // 获取用户会话在redis中对应的所有购物项
        CartVo cart = getCart();

        return cart.getItems().stream()
                .filter(CartItemVo::getCheck)
                .collect(Collectors.toList());
    }


    // 获取用户会话在redis中对应的key
    private String getCartKey() {
        // 获取用户会话信息
        UserInfoTo userInfo = CartUserKeyCheckInterceptor.threadLocal.get();

        if (StrUtil.isBlank(userInfo.getUserKey()) && userInfo.getUserId() == null) return null;

        if (userInfo.getTempUser()) {
            // 临时用户
            return "cart:user:" + userInfo.getUserKey();
        }

        // 登录用户
        return "cart:user:" + userInfo.getUserId();
    }

    // 检查用户是否有临时购物项
    @SneakyThrows
    private void checkUserCookieCart() {
        // 获取用户会话信息
        UserInfoTo userInfo = CartUserKeyCheckInterceptor.threadLocal.get();

        // 登录用户 的 临时会话是否为空 - 反正每次请求都要判断 - 没办法 - 要么清除临时user-key
        if (StrUtil.isNotBlank(userInfo.getUserKey()) && userInfo.getUserId() != null) {
            // 临时用户redis key
            String tempCartKey = "cart:user:" + userInfo.getUserKey();
            // 登录用户redis key
            String loginCartKey = "cart:user:" + userInfo.getUserId();
            // 获取 操作hash
            BoundHashOperations<String, String, String> tempCartKeyOps = stringRedisTemplate.boundHashOps(tempCartKey);
            BoundHashOperations<String, String, String> loginCartKeyOps = stringRedisTemplate.boundHashOps(loginCartKey);
            // 获取所有 row 值
            List<String> values = tempCartKeyOps.values();
            if (CollUtil.isNotEmpty(values)) {
                // 临时用户有购物项
                assert values != null; // 断言不为空 - 其实就是非null CollUtil.isNotEmpty(values)
                for (String value : values) {
                    // 获取临时用户购物项
                    CartItemVo cartItem = objectMapper.readValue(value, CartItemVo.class);
                    // 添加到登录用户购物项
                    String loginCartKeyOpsByCartItemSkuId = loginCartKeyOps.get(cartItem.getSkuId().toString());
                    if (StrUtil.isBlank(loginCartKeyOpsByCartItemSkuId)) {
                        // 临时用户的购物项在 - 登录用户购物项中没有
                        loginCartKeyOps.put(cartItem.getSkuId().toString(), value);
                        continue;
                    }
                    // 临时用户的购物项在 - 登录用户购物项中有
                    CartItemVo loginCartItem = objectMapper.readValue(loginCartKeyOpsByCartItemSkuId, CartItemVo.class);
                    // 登录用户的购物项数量累加
                    loginCartItem.setCount(loginCartItem.getCount() + cartItem.getCount());
                    // 重新写入登录用户购物项
                    loginCartKeyOps.put(loginCartItem.getSkuId().toString(), objectMapper.writeValueAsString(loginCartItem));
                }
                // 清空临时用户购物项
                stringRedisTemplate.delete(tempCartKey);
            }

            // 设置 '删除临时用户标识' 的标识
            CartUserKeyCheckInterceptor.threadLocal.get().setRemoveTempUser(true);
        }

    }
}
